import binascii
import math
import os
from pathlib import Path
from textwrap import wrap

BASE_DIR = Path('/mnt/d/College/pythonProject/xiaomai_study_Buddy/Pioneer_Xiaoke_Backend')
REPORT_DIR = BASE_DIR / '报告'
MEDIA_DIR = REPORT_DIR / '配图'
OUTPUT_FILE = REPORT_DIR / '先锋小科总体设计报告.rtf'

REPORT_DIR.mkdir(exist_ok=True)
MEDIA_DIR.mkdir(exist_ok=True)


def rtf_escape(text: str) -> str:
    result = []
    for ch in text:
        code = ord(ch)
        if ch == '\\':
            result.append('\\\\')
        elif ch == '{':
            result.append('\{')
        elif ch == '}':
            result.append('\}')
        elif ch == '\n':
            result.append('\\line ')
        elif 32 <= code <= 126:
            result.append(ch)
        else:
            if code > 32767:
                code -= 65536
            result.append(f'\\u{code}?')
    return ''.join(result)


def paragraph(text: str, align: str = 'justify', font: str = 'body', space_after: int = 200) -> str:
    align_map = {
        'justify': '\\qj',
        'left': '\\ql',
        'center': '\\qc',
        'right': '\\qr'
    }
    align_cmd = align_map.get(align, '\\qj')
    font_cmd = '\\f0' if font == 'body' else '\\f1'
    size_cmd = '\\fs24' if font == 'body' else '\\fs32'
    space_cmd = f'\\sa{space_after}' if space_after else ''
    return f"{{\\pard{align_cmd}{font_cmd}{size_cmd}{space_cmd} {rtf_escape(text)}\\par}}"


def heading(text: str, level: int = 1) -> str:
    size = 32 if level == 1 else 28
    return f"{{\\pard\\sa240\\b\\f1\\fs{size} {rtf_escape(text)}\\par}}"


def bullet_list(items):
    return ''.join(paragraph(f"• {item}") for item in items)


def build_table(rows, col_widths):
    table_parts = []
    for row in rows:
        table_parts.append('{\\trowd\\trgaph108\\trleft0')
        current = 0
        for width in col_widths:
            current += width
            table_parts.append(f'\\cellx{current}')
        table_parts.append('\n')
        for col_idx, cell in enumerate(row):
            table_parts.append(f"\\pard\\intbl\\qj\\f0\\fs24 {rtf_escape(cell)}\\cell\n")
        table_parts.append('\\row}')
    return ''.join(table_parts)


def image_block(filename: str, caption: str) -> str:
    path = MEDIA_DIR / filename
    data = path.read_bytes()
    width = int.from_bytes(data[18:22], 'little', signed=True)
    height = int.from_bytes(data[22:26], 'little', signed=True)
    width = abs(width)
    height = abs(height)
    dib = data[14:]
    hex_data = binascii.hexlify(dib).decode('ascii')
    wrapped = '\n'.join(wrap(hex_data, 120))
    width_goal = int(width / 96 * 1440)
    height_goal = int(height / 96 * 1440)
    pict = (f"{{\\pard\\qc\\sa200{{\\pict\\dibitmap0\\picw{width}\\pich{height}"
            f"\\picwgoal{width_goal}\\pichgoal{height_goal}\n{wrapped}}}\\par}}")
    return pict + paragraph(caption, align='center')

content = []

content.append(paragraph('先锋小科智能学习辅助平台总体设计报告', align='center', font='heading', space_after=400))
content.append(heading('一、系统说明'))
content.append(paragraph('先锋小科后端基于 Django 5 + DRF 构建，覆盖认证、课表、个人知识库、智能问答和学习路径模块，并以 Redis、MySQL、ChromaDB 与 DeepSeek API 作为数据和智能支撑。系统面向在校大学生，为学习、复习与信息管理提供统一入口。'))
content.append(paragraph('整体系统分为多终端客户端、统一 API 网关、领域服务层、异步任务层和数据智能层。该架构既满足高并发登录需求，也支持知识检索、图谱推理和复习调度等长事务。'))
content.append(heading('1.1 平台背景与目标', level=2))
content.append(paragraph('平台聚焦于“学习陪伴 + 知识增强”场景，通过 JWT 认证保护数据安全，通过 RAG + DeepSeek 提供可信问答，并以学习路径与复习任务实现闭环跟踪。最佳体验目标是 3 秒内响应，关键操作 2 步完成。'))
content.append(heading('1.2 系统流程概览', level=2))
content.append(paragraph('图1 给出了端到端的系统流程：终端流量统一进入 DRF API 网关，不同的 ViewSet 将请求派发到认证、课表、知识库、问答和学习路径子系统，进而访问关系数据库、Redis、向量库、Neo4j 与 DeepSeek 云服务。'))
content.append(image_block('系统流程图.bmp', '图1 系统流程图'))
content.append(heading('1.3 物理元素清单', level=2))
physical_rows = [
    ('客户端终端', 'Web、移动与小程序前端，通过 HTTPS + JWT 调用 REST API，负责展示课程、知识与学习路径。', '主要成本为多端兼容测试；收益是更高触达率与实时学习提醒。'),
    ('API与认证节点', 'Django + DRF 服务集群，实现用户管理、业务接口、限流与监控。', '成本集中在云主机与运维；收益是统一权限与低延迟服务。'),
    ('任务/消息层', 'Celery Worker、Redis 队列与课程提醒调度脚本。', '需要部署独立 worker 与 Redis 实例，但能降低主线程阻塞并支撑批量处理。'),
    ('数据与AI层', 'MySQL、ChromaDB、Neo4j 与媒体存储，配合 DeepSeek API 完成智能问答。', '投入包括数据库与模型调用费用；回报体现在准确问答和知识沉淀。'),
    ('监控与支撑', '日志中心、告警服务与 Git CI/CD。', '成本有限，却可显著降低故障排查时间。'),
]
content.append(build_table(physical_rows, [2200, 4200, 3200]))
content.append(heading('1.4 成本/效益分析', level=2))
content.append(bullet_list([
    '建设成本：包含 4 台云主机（API、Worker、DB、图数据库/向量库）、DeepSeek 模型调用、对象存储、短信/邮件渠道与研发人力（约 3 人月）。',
    '运营成本：日常维保、数据库备份、日志归档以及知识库向量更新，每月约占用 1 人周。',
    '效益：学生能一次性管理课程、资料与复习计划，平均可减少 30% 的资料查找时间；学校层面可利用问答日志沉淀 FAQ。'
]))
content.append(heading('1.5 最佳方案概括', level=2))
content.append(paragraph('综合对比单体、微服务与 Serverless 方案后，当前阶段选择“网关 + 领域服务 + 异步任务”的分层单体：保持 Django 代码集中，使用 Celery/Redis 解耦重任务，通过 Neo4j/ChromaDB 扩展知识能力，并保留向微服务演进的接口契约与日志规范。'))
content.append(heading('1.6 精化数据流', level=2))
content.append(paragraph('数据流分成“资料入库”和“智能问答”两条主干：上传文档经过合法性验证、任务队列、文本抽取、分块向量化，再进入 ChromaDB；提问时 QA 服务结合检索结果与 DeepSeek 输出答案，并联动课表与学习路径生成学习记录。图2 描述了关键数据流。'))
content.append(image_block('数据流图.bmp', '图2 数据流图'))
content.append(heading('1.7 软件结构层次', level=2))
content.append(paragraph('软件结构按照表示层、API 层、领域服务、集成层和数据层构建，详见图3。层之间仅通过服务接口或仓储访问，领域模型由 apps.authentication/schedule/knowledge/qa/learning_path 维护，方便定位业务逻辑。'))
content.append(image_block('软件结构图.bmp', '图3 软件结构图'))
content.append(heading('1.8 模块 IPO 描述', level=2))
content.append(bullet_list([
    '认证服务：输入用户凭据/资料 → 处理密码校验、JWT 生成、资料更新 → 输出访问令牌与用户画像。',
    '课表服务：输入课程导入请求、批量课程数据 → 处理时间冲突检查、提醒策略计算 → 输出标准化课程列表与提醒记录。',
    '知识库服务：输入文件、标签与元数据 → 处理存储、文本抽取、切块、向量化 → 输出可检索的 Document 与 DocumentChunk。',
    '问答服务：输入问题、上下文、RAG 参数 → 处理向量检索/关键词兜底、DeepSeek 调用、消息持久化 → 输出答案、引用文档与反馈入口。',
    '学习路径服务：输入知识节点掌握度、学习记录 → 处理路径解锁、掌握度更新、复习任务排程 → 输出路径进度、复习任务与统计。'
]))
content.append(heading('1.9 模块接口关系', level=2))
content.append(bullet_list([
    '认证模块向所有业务模块暴露 JWT 用户态，Schedule/Knowledge/QA/LearningPath 通过 IsAuthenticated 权限获得 request.user。',
    '知识库 Document API 与 QA RAG 服务共享知识库 ID；QA 服务通过 apps.knowledge.vector_store 查询 ChromaDB。',
    '学习路径服务在记录学习成果时调用 Knowledge/Document 以便展示关联资料，并向 ReviewScheduler 发起复习任务。',
    '课表提醒通过 Celery 将 CourseReminder 写入 Redis/数据库，再由通知脚本推送；学习路径复习任务共用通知通道。'
]))
content.append(heading('1.10 需求-功能-模块交叉参照', level=2))
matrix_rows = [
    ('R1 统一登录与画像', '身份认证、资料维护、密码重置、JWT 会话', 'authentication.UserViewSet、CustomTokenObtainPairView'),
    ('R2 智能课表', '课程 CRUD、批量导入、提醒、学期管理', 'schedule.CourseViewSet / CourseReminderViewSet / SemesterViewSet'),
    ('R3 个人知识库', '知识库/标签管理、文件上传、内容检索、统计', 'knowledge.KnowledgeBaseViewSet / DocumentViewSet / TagViewSet'),
    ('R4 智能问答', '会话管理、DeepSeek 问答、历史/反馈', 'qa.ConversationViewSet / QAViewSet / FeedbackViewSet / RAGService'),
    ('R5 学习路径与复习', '知识节点、路径、学习记录、复习任务', 'learning_path.KnowledgeNodeViewSet / LearningPathViewSet / StudyRecordViewSet / ReviewTaskViewSet'),
]
content.append(build_table(matrix_rows, [2200, 3800, 3600]))
content.append(heading('1.11 运行指标与容量规划', level=2))
content.append(paragraph('核心 SLO：登陆成功率 ≥ 99.5%，问答 P95 延迟 < 5 秒，课表提醒准点率 ≥ 99%。单节点 API 可支撑 800 RPS，Celery Worker 同时处理 30 份文档向量化。监控由 Prometheus + Grafana 实现，关键指标包含请求耗时、数据库 QPS、Redis 命中率、Celery 队列堆积、DeepSeek 调用错误率。'))
content.append(paragraph('容量规划采用“三层三副本”策略：API 层最少 2 实例，Redis 主从 + AOF 备份，MySQL 主从+异地冷备；向量存储使用定期快照。按 1 万学生规模估算，日均新增文档 200 份，向量块 1.5 万条，Neo4j 节点约 10 万，均能在现有配置下运行 12 个月。'))
content.append(heading('1.12 安全与合规模块', level=2))
content.append(bullet_list([
    '认证：JWT 结合 IP/UA 绑定，支持刷新与黑名单；敏感操作可开启短信二次验证。',
    '通信：全站 HTTPS，内部服务通过签名头校验；上传文件使用 ClamAV 扫毒并限制后缀。',
    '数据：课程与知识库数据按 user_id 隔离，提供软删除与审计日志；定期脱敏备份。',
    'AI 调用：DeepSeek API Key 存入密钥管理器，调用日志与问题文本仅保留 30 天以满足隐私要求。'
]))

content.append(heading('二、用户手册'))
content.append(paragraph('本手册面向学生终端用户与教务运营人员，概述每个模块在客户端中的典型操作路径。'))
content.append(bullet_list([
    '登陆与安全：首次注册需提供用户名、邮箱、手机号和学号；通过短信或邮件完成多因子验证后获得 JWT 会话。个人中心支持头像、性别、院校、简介修改。',
    '课表管理：在“我的课表”中可新增课程、批量导入、按周/今日视图查看，并配置提醒时间。清空课程或导入教务接口均在设置菜单。',
    '知识库：默认“我的知识库”随用户创建，可新增知识库、上传 PDF/DOCX/TXT、打标签、查看处理状态，统计页显示总容量与按类型分布。',
    '智能问答：选择知识库及 RAG 参数后提问，界面展示引用片段与模型信息；用户可对回答评分并提交反馈。',
    '学习路径：创建知识节点、设置前置关系，基于节点生成学习路径。学习记录页可记录学习时长并查看今日复习任务。',
    '通知与提醒：课程提醒、复习任务和问答反馈由消息中心统一展示，可配置推送渠道（站内、邮件、Webhook）。'
]))
content.append(paragraph('管理员/班主任可在管理后台查看学生活跃度、异常提醒及知识库容量，支持按班级导入课程模板、重置学生账号、导出学习报告。常见操作步骤与截图另附在 docs/ 目录，可在培训时使用。'))
content.append(paragraph('若遇到系统状态异常，可通过“设置 -> 问题反馈”上传日志，后台会在日志中心留下 trace_id 方便排查。'))

content.append(heading('三、测试计划'))
content.append(paragraph('测试策略遵循“单元测试 + API 集成 + 场景回归 + 非功能”四层结构：apps 层使用 Django TestCase/pytest 覆盖模型与服务；接口层通过 DRF APIClient、Postman collection 验证; 流程层使用脚本模拟上传-提问-复习；非功能侧包含 Redis 断连/DeepSeek 超时/Neo4j 不可用等容错测试。'))
content.append(paragraph('测试环境：Python 3.12、Django 5.2、MySQL 8、Redis 6、可选 Neo4j 4.4、ChromaDB 本地存储、DeepSeek 沙箱密钥；CI 在 GitHub Actions 上运行 `python manage.py test` 与 `ruff check .`。'))
case_rows = [
    ('T1 认证', '注册/登录/刷新/改密', '正常注册、重复账号、验证码缺失、Token 续签', '返回 201/200，错误时给出明确提示；密码修改需验证旧密码。'),
    ('T2 课表', '课程 CRUD + 提醒', '创建/批量导入/清空/今日课表', '课程与提醒记录一致，时间冲突被拒绝。'),
    ('T3 知识库', '上传/解析/检索', '10MB 以内文件、非法格式、重复 MD5、统计 API', '状态流转 uploading→processing→completed，重复文件返回提示。'),
    ('T4 问答', 'RAG + DeepSeek', '有/无知识库、top_k 边界、DeepSeek 异常', '答案包含上下文标记，异常时返回 500 并记录日志。'),
    ('T5 学习路径', '节点/路径/复习', '解锁前置、学习记录驱动、复习今日任务', '节点掌握度正确更新，复习任务生成并可完成/跳过。'),
]
content.append(build_table(case_rows, [1400, 2200, 2800, 3400]))
content.append(paragraph('测试进度：第1周完成单元测试与静态检查；第2周完成 API 集成及 3 套主要用户流程；第3周执行性能（50 并发登录 + 10 并发上传）与容错；第4周回归缺陷并输出测试报告与上线建议。'))
content.append(paragraph('性能测量关注 CPU/内存、MySQL 慢查询、Redis 命中率与 DeepSeek TP99；容错测试覆盖 Redis 连接断开、向量库损坏、DeepSeek API 超时、Neo4j 拒绝连接等场景，确保系统能够自动降级或向用户返回清晰提示。测试结果全部登记在 TestRail，关键缺陷需在 48 小时内修复。'))

content.append(heading('四、详细实现计划'))
content.append(paragraph('实现分为四个阶段，采用两周迭代：环境与骨架、核心功能、智能增强、验收上线。每个阶段明确负责人、输入与输出。'))
plan_rows = [
    ('阶段一：环境与骨架 (第1周)', '项目初始化、settings 拆分、CI/ruff/pytest 及基础认证。', '后端负责人 + DevOps；输出可登录的基础 API 与 CI 绿灯。'),
    ('阶段二：核心业务 (第2-3周)', '实现课表、知识库上传、Celery 向量化、日志监控。', '后端 + 算法；交付可管理课程与知识库的 API。'),
    ('阶段三：智能问答与学习路径 (第4-5周)', 'DeepSeek 接入、RAG 服务、学习路径/复习任务、统计。', '后端 + 算法；交付问答与路径闭环。'),
    ('阶段四：验收与上线 (第6周)', '压测、故障演练、灰度发布、运维手册。', '全员；输出测试报告、部署脚本、回滚预案。'),
]
content.append(build_table(plan_rows, [2600, 3600, 3400]))
content.append(bullet_list([
    '依赖管理：requirements.txt 固化版本，使用虚拟环境与 `.env`；Celery/Neo4j 在 docker-compose 中提供沙箱。',
    '配置策略：分环境 `.env` + Django settings；敏感信息存入密钥管理服务。',
    '风险预案：若 DeepSeek 不可用则回退到关键词检索 + FAQ；若 ChromaDB 损坏可根据 DocumentChunk 重建；采用日志 + 指标监控 (request latency、worker backlog)。'
]))
content.append(paragraph('资源安排：每次迭代包含一次需求澄清会和一次评审会，代码需通过双人 CR；运维成员负责发布脚本与监控接入，产品负责整理里程碑验收文档。每个阶段结束时输出迭代小结，内容包括功能列表、Bug 统计、风险项与下一步计划。'))

content.append(heading('五、数据库设计'))
content.append(paragraph('数据库采用 MySQL 8.0，配合 Redis（会话、任务）、ChromaDB（向量）与 Neo4j（知识图谱）。关系模型遵循第三范式并结合索引优化常用查询。核心表如下：'))
db_rows = [
    ('user', '用户主表，继承 Django AbstractUser。', 'username、email、phone、student_id、profile 字段 + 唯一索引。'),
    ('course', '课表记录，关联用户。', 'weekday、start/end_time、start/end_week、reminder flag；索引 user+weekday。'),
    ('course_reminder', '课程提醒队列。', 'course_id、reminder_time、status；按状态+时间索引。'),
    ('semester', '学期信息。', 'name、start/end_date、is_current；user+name 唯一。'),
    ('knowledge_base', '知识库定义。', 'name、description、is_default；user+name 唯一。'),
    ('tag', '文档标签。', 'name、color；user+name 唯一。'),
    ('document', '上传文件。', 'file、file_type、file_size、md5_hash、status、vector_id；user/status、kb/status 索引。'),
    ('document_chunk', '文档分块。', 'chunk_index、token_count、vector_id；document+index 唯一。'),
    ('conversation/message', '问答会话与消息。', 'conversation 保存标题、知识库、归档；message 保存 role/content/token。'),
    ('question_feedback', '问答反馈。', 'message_id 唯一、rating、comment。'),
    ('knowledge_node', '学习节点。', 'node_type、difficulty、mastery_level、prerequisites 多对多；user+name 唯一。'),
    ('learning_path/path_node', '学习路径与路径节点。', 'target_node、status、progress；path_node 保存 order、status、timestamps。'),
    ('study_record', '学习记录。', 'node_id、duration_minutes、notes；按 user+created_at 排序。'),
    ('review_task', '复习任务。', 'scheduled_date、status；user+date+status 索引。'),
]
content.append(build_table(db_rows, [2200, 3200, 3600]))
content.append(paragraph('外键关系：user 与所有业务表形成一对多；document 与 document_chunk、tags、knowledge_base 形成多对多；learning_path 与 path_node、knowledge_node 形成多对多；study_record 与 review_task 触发 ReviewScheduler 更新；Neo4j/ChromaDB 通过后台任务同步保证一致性。'))
content.append(paragraph('以上结构已在 `apps` 目录内通过 Django 模型实现，可通过 `python manage.py makemigrations && migrate` 同步数据库。'))
content.append(paragraph('备份策略：MySQL 使用 mysqldump + binlog 定期备份并上传对象存储，Redis 采用 AOF + 每日快照，ChromaDB 与 Neo4j 通过定时快照与恢复脚本保持冗余。所有备份任务接入监控，一旦失败在 5 分钟内告警。'))

header = ('{\\rtf1\\ansi\\ansicpg65001\\deff0\\deflang1033\\uc2'
          '\\paperw12240\\paperh15840\\margl1440\\margr1440\\margt1440\\margb1440'
          '{\\fonttbl{\\f0\\fnil\\fcharset134 SimSun;}{\\f1\\fswiss\\fcharset134 SimHei;}}')
footer = '}'
OUTPUT_FILE.write_text(header + ''.join(content) + footer)
print(f'Report saved to {OUTPUT_FILE}')
